/** 
 * @addtogroup j1939tp
 * j1939tp
 * @brief j1939 数据传输层
 * 
 * 本模块提供了J1939服务接收单帧和多帧数据的统一方法。
 * J1939 网络协议规范定义的数据帧中，数据长度不能超过8个字节。
 * 在实际的应用中，ECU的信息及诊断报文等信号传输的数据长度会超过十
 * 几个字节甚至达到上百字节。在传输长度大于8字节的消息时，必须使用
 * J1939-21多包数据传输协议
 *
 * @code{c}
    // J1939完整包数据通知回调
    static Int32 DemocaseIndicationEvent)(void *sender, 
        TJ1939ResultEnum result, const TJ1939DcmMsg *dcm_msg)
    {
            // 解析业务 ... 
    }
    
    // CAN 数据发送接口
    static Int32 DemocaseSendMsg(void *sender, const TJ1939CanMsg *msg)
    {
        //  ...
    }

    int main()
    {
        UInt8 self_addr = 0x4A; // 本机地址
        UInt8 peer_addr = 0x00; // 对方地址
        UInt32 cm_num = 32;     // 最大支持连接数
        TJ1939Tp j1939_tp;
        
        // 创建连接
        TJ1939TpCreate(&j1939_tp, self_addr, peer_addr, cm_num);
        
        // 设置事件方法
        TJ1939TpSetEventMethod(&j1939_tp, NULL, 
            DemocaseIndicationEvent, DemocaseSendMsg);
        
        while(1)
        {
            // 循环调度
            TJ1939TpRunOnce(&j1939_tp, curr_tick);
        }
        
        // 结束，析构连接
        TJ1939TpDestroy(&j1939_tp);
    }
    
    can_thread:
        // 接收CAN原始数据
        TJ1939CanMsg msg;
        DoReadCanMsg(&msg);
        
        // 填充给J1939连接
        TJ1939TpRxIndication(&j1939_tp, &msg)
        // ....
        
    user_thead:
        // 主动请求PGN
        TJ1939TpRequestPgn(&j1939_tp, kJ1939CanxChn0, 0xD300, 0x00);
        // ...

 * @endcode
 * 
 * @{
 * @file         j1939_tp.h
 * @brief        J1939 数据传输层接口定义
 * @note
 * @author       lixinliang
 *
 * @par          修改日志
 * <table>
 * <tr><th>Date        <th>Version               <th>Author                     <th>Description
 * <tr><td>2019-09-20  <td>1.0                   <td>lixinliang@hopechart.com   <td>创建初始版本
 * <tr><td>2020-08-10  <td>1.1                   <td>Akee                       <td>接口优化
 * @copyright    杭州鸿泉物联网技术股份有限公司
 */

#ifndef _J1939_TP_H_
#define _J1939_TP_H_

#include "ofc/ofc_object.h"
#include "ofc/ofc_list.h"
#include "j1939/j1939_common.h"

#ifdef __cplusplus
extern "C"
{
#endif

/**
 * 通知CAN结果事件
 * @param sender: 操作对象
 * @param result: 通知状态结果 参见 j1939_common.h
 * @param dcm_msg: 1939组件处理后的数据消息
 * @return 消息处理结果 0: 成功 -1:失败 参考 TJ1939ResultEnum
 */
typedef Int32 (*TJ1939TpDoIndicationEvent)(void *sender, TJ1939ResultEnum result, const TJ1939DcmMsg *dcm_msg);

/**
 * 发送CAN消息接口
 * @param sender: 操作对象
 * @param msg:    CAN原始消息
 * @return 消息发送结果 0: 成功 -1:失败 参考 TJ1939ResultEnum
 */
typedef Int32 (*TJ1939TpDoSendMsgIf)(void *sender, const TJ1939CanMsg *msg);

/**
 * EA请求消息接收事件
 * @param sender: 操作对象
 * @param msg:    CAN原始消息
 * @return 消息发送结果 0: 成功 -1:失败 参考 TJ1939ResultEnum
 */
typedef Int32 (*TJ1939TpDoRecReqEvent)(void *sender, const TJ1939CanMsg *msg);

/**
 * 发送多帧状态通知事件
 * @param sender: 操作对象
 * @param status: 多帧发送状态
 * @param tx_msg: 发送状态通知结构
 * @return 消息发送结果 0: 成功 -1:失败 参考 TJ1939ResultEnum
 */
typedef Int32 (*TJ1939TpDoTxNotifyEvent)(void *sender, Int32 status, TJ1939TxMsg *tx_msg);

/**
 * J1939数据链路层，传输单帧和多帧
 */
typedef struct TJ1939TpTag TJ1939Tp;
struct TJ1939TpTag
{
PRIVATE
    TOfList  cm_list_;          ///< CM连接 
    UInt32   cm_max_;           ///< 连接的最大个数

    TOfList  broadcast_list_;   ///< 广播队列
    TOfList  p2p_list_;         ///< P2P队列

    UInt8    broadcast_cap_;    ///< 广播队列容量
    UInt8    p2p_cap_;          ///< P2P队列容量
    UInt8    self_addr_;        ///< 本机地址
    UInt8    peer_addr_;        ///< 对端地址

PRIVATE
    void *receiver_;                           ///< 事件接收者
    TJ1939TpDoIndicationEvent  on_indication_; ///< 消息接收事件
    TJ1939TpDoSendMsgIf        can_send_if_;   ///< CAN报文发送接口
    TJ1939TpDoRecReqEvent      on_rec_req_;    ///< EA请求消息接收事件
    TJ1939TpDoTxNotifyEvent    on_tx_notify_;  ///< 发送状态事件通知
};

/**
 * J1939TP层构造函数
 * @param self: 操作对象
 * @param self_addr: 自己TP层设置地址 不可为0xFF,如果设置为0xFF，则默认0x4A
 * @param peer_addr: 连接的对端地址 若为0xFF, 表示接收全部地址，即接收总线上所有广播的数据，包括单包和多包
 * @param cm_num:    最大支持同时接收多包连接的数量
 * @return
 */
void TJ1939TpCreate(TJ1939Tp *self, UInt8 self_addr, UInt8 peer_addr, UInt32 cm_num);

/**
 * J1939TP层析构函数
 * @param self: 操作对象
 * @return
 */
void TJ1939TpDestroy(TJ1939Tp *self);

/**
 * 设置事件方法
 * @param self: 操作对象
 * @param object: 外部对象指针
 * @param indication: CAN数据接收通知事件
 * @param send_if: CAN数据发送方法
 * @return
 */
void TJ1939TpSetEventMethod(TJ1939Tp *self, void *object, 
    TJ1939TpDoIndicationEvent indication, TJ1939TpDoSendMsgIf send_if);

/**
 * 设置EA请求事件回调方法
 * @param self: 操作对象
 * @param object: 外部对象指针
 * @param rec_req: EA消息接收通知事件
 * @return
 */
void TJ1939TpSetRecReqEventMethod(TJ1939Tp *self, void *object, TJ1939TpDoRecReqEvent rec_req);

/**
 * 设置发送多帧状态通知方法
 * @param self: 操作对象
 * @param object: 外部对象指针
 * @param tx_notify: 发送多帧状态通知事件
 * @return
 */
void TJ1939TpSetTxNotifyMethod(TJ1939Tp *self, void *object, TJ1939TpDoTxNotifyEvent tx_notify);

/**
 * 向 CAN 总线请求对应PGN码的数据
 * @param self: 操作对象
 * @param can_channel: CAN 通道 参见 j1939_common.h
 * @param pgn: PGN
 * @param dest_addr: 目标地址
 * @param self_addr: 源地址
 * @return 0: 成功 -1:失败 参考 TJ1939ResultEnum
 */
Int32 TJ1939TpRequestPgn(TJ1939Tp *self, TJ1939CanxChnEnum channel, UInt32 pgn, UInt8 dest_addr, UInt8 self_addr);

/**
 * 主动发送J1939的数据流，内部会自动处理多帧还是单帧[暂未实现]
 * @todo 此函数暂未实现
 * @param self: 操作对象
 * @param channel: 通道号，参考 TJ1939CanxChnEnum
 * @param pgn: PGN
 * @param dest_addr: 目标地址
 * @param data: 数据内容
 * @param data_len: 数据内容长度
 * @return  0: 成功 -1:失败  参考 TJ1939ResultEnum
 */
Int32 TJ1939TpSendStream(TJ1939Tp *self, TJ1939CanxChnEnum channel, 
    UInt32 pgn, UInt8 dest_addr, const void *data, Int32 data_len);

/**
 * 主动发送透传数据 仅支持单帧
 * @param self: 操作对象
 * @param msg: 数据结构
 * @return 0: 成功 -1:失败  参考 TJ1939ResultEnum
 */
Int32 TJ1939TpThroughStream(TJ1939Tp *self, TJ1939CanMsg *msg);

/**
 * CAN 原始数据接收，收到底层的CAN数据后，传入到该接口中
 * @param self: 操作对象
 * @param msg: CAN 消息
 * @return
 */
void TJ1939TpRxIndication(TJ1939Tp *self, const TJ1939CanMsg *msg);

/**
 * CAN 循环调度处理, 主要处理内部超时采集超时逻辑
 * @param self: 操作对象
 * @param tick: 当前tick
 * @return
 */
void TJ1939TpRunOnce(TJ1939Tp *self, UInt32 tick);

/**
 * 获取PGN
 * @param can_id: can消息ID
 * @return pgn
 */
UInt32 J1939GetPgn(UInt32 can_id);

/**
 * 获取源地址
 * @param can_id: can消息ID
 * @return 源地址
 */
UInt8 J1939GetSrc(UInt32 can_id);

/**
 * 目标源地址
 * @param can_id: can消息ID
 * @return 目标地址
 */
UInt8 J1939GetDest(UInt32 can_id);

/**
 * 设置列表最大个数
 * @param self: 操作对象
 * @param type:  多帧发送类型  参考 TJ1939MultiTypeEnum
 * @param count: 个数
 * @return 设置是否成功
 */
Bool J1939SetListCount(TJ1939Tp *self, TJ1939MultiTypeEnum type, Int32 count);

/**
 * 获取列表容量
 * @param self: 操作对象
 * @param type:  多帧发送类型  参考 TJ1939MultiTypeEnum
 * @return 列表容量 
 */
Int32 J1939GetListCapacity(TJ1939Tp *self, TJ1939MultiTypeEnum type);

#ifdef __cplusplus
}  // extern "C"
#endif

#endif // _J1939_TP_H_

/** @} */
